﻿using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using XRpgLibrary;
using XRpgLibrary.GraphicsClasses;
using XRpgLibrary.TileEngine;

namespace XRpgLibrary.InterfaceClasses
{
    public enum MenuPlacementY
    {
        Top,
        Center,
        Bottom
    }

    public enum MenuPlacementX
    {
        Left,
        Center,
        Right
    }

    enum BorderSides
    {
        Top,
        Bottom,
        Left,
        Right
    }

    enum BorderCorners
    {
        NW = 4,
        NE,
        SW,
        SE
    }

    public class BaseMenuComponent : DrawableGameComponent, IGameDrawableMenu, IGameUpdateableMenu
    {
        public event PauseStateChangedEventHandler PauseStateChanged;

        #region Fields
        protected const int BORDER_WIDTH = 16;
        int SBorderYCoord;
        int EBorderXCoord;
        
        protected SpriteBatch spriteBatch;

        Rectangle screenRect;
        Rectangle borderRectangle;
        Texture2D backgroundTexture;
        Texture2D borderTexture;
        Texture2D menuBorderSourceTexture;
        SpriteFont menuFont;
        
        #endregion

        #region Properties

        public bool DisplayBorders { get; set; }
        public Rectangle MenuRectangle;
        public bool IsModal { get; set; }
        public bool IsAlive { get; set; }

        Rectangle borderCellDestRect;
        Rectangle BorderCellDestRect
        {
            get
            {

                if (borderCellDestRect.Width == 0)
                    borderCellDestRect = new Rectangle(
                        BorderRectangle.X,
                        BorderRectangle.Y,
                        BORDER_WIDTH,
                        BORDER_WIDTH);

                return borderCellDestRect;
            }
        }

        Rectangle BorderRectangle
        {
            get
            {
                if (borderRectangle.Width < 1)
                    borderRectangle = new Rectangle(
                        MenuRectangle.X - BORDER_WIDTH,
                        MenuRectangle.Y - BORDER_WIDTH,
                        MenuRectangle.Width + BORDER_WIDTH,
                        MenuRectangle.Height + BORDER_WIDTH);

                return borderRectangle;
            }
            set
            {
                borderRectangle = value;
            }
        }

        protected Texture2D MenuBorderSourceTexture
        {
            get
            {
                if (menuBorderSourceTexture == null)
                    menuBorderSourceTexture = Game.Content.Load<Texture2D>(@"GUI\menuElements");

                return menuBorderSourceTexture;
            }
        }

        protected Texture2D BackgroundTexture
        {
            get
            {
                if (backgroundTexture == null)
                {
                    GradientTexture2D texture = new GradientTexture2D(
                        Game.GraphicsDevice,
                        MenuRectangle.Width - BORDER_WIDTH / 2,
                        MenuRectangle.Height - BORDER_WIDTH / 2,
                        new Color(150, 178, 188, 255),
                        new Color(30, 61, 76, 255));

                    backgroundTexture = (Texture2D)texture;
                }
                return backgroundTexture;
            }
        }

        protected Texture2D BorderTexture
        {
            get
            {
                if (borderTexture == null)
                    borderTexture = BuildBorderTexture();
                
                return borderTexture;
            }
        }

        protected SpriteFont MenuFont
        {
            get
            {
                if (menuFont == null)
                    menuFont = Game.Content.Load<SpriteFont>(@"Fonts\ControlFont");

                return menuFont;
            }
        }

        protected Dictionary<int, Rectangle> BorderSourceRectangles
        {
            get;
            set;
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a component that displays a box for use with dialog and menus
        /// </summary>
        /// <param name="game"></param>
        /// <param name="destRect"></param>
        public BaseMenuComponent(RpgGame game, Rectangle destRect, bool isModal)
            : base(game)
        {
            MenuRectangle = destRect;
            IsModal = isModal;
            Enabled = false;
            DisplayBorders = true;
            IsAlive = true;
            this.spriteBatch = game.componentSpriteBatch;
            this.screenRect = game.ScreenRectangle;

            SnapMenuSize();
            GenerateBorderSourceRectangles();

            if (IsModal)
                OnPauseStateChanged(Enabled);
        }

        public BaseMenuComponent(RpgGame game, Rectangle destRect)
            : this(game, destRect, true)
        { }

        public BaseMenuComponent(RpgGame game, int x, int y, int width, int height)
            : this(game, new Rectangle(x, y, width, height), true)
        { }

        public BaseMenuComponent(RpgGame game, MenuPlacementX x, MenuPlacementY y, int width, int height)
            : this(game, new Rectangle(0, 0, width, height), true)
        {
            SetPosition(x, y);
        }

        public BaseMenuComponent(RpgGame game, int x, int y, int width, int height, bool isModal)
            : this(game, new Rectangle(x, y, width, height), isModal)
        { }

        public BaseMenuComponent(RpgGame game, MenuPlacementX x, MenuPlacementY y, int width, int height, bool isModal)
            : this(game, new Rectangle(0, 0, width, height), isModal)
        {
            SetPosition(x, y);
        }


        #endregion

        #region Helper Methods

        private void GenerateBorderSourceRectangles()
        {
            BorderSourceRectangles = new Dictionary<int, Rectangle>();

            BorderSourceRectangles.Add(
                (int)BorderSides.Top,
                new Rectangle(
                    BORDER_WIDTH,
                    0,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));

            BorderSourceRectangles.Add(
                (int)BorderSides.Bottom,
                new Rectangle(
                    BORDER_WIDTH,
                    BORDER_WIDTH * 3,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));

            BorderSourceRectangles.Add(
                (int)BorderSides.Left,
                new Rectangle(
                    0,
                    BORDER_WIDTH,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));

            BorderSourceRectangles.Add(
                (int)BorderSides.Right,
                new Rectangle(
                    BORDER_WIDTH * 3,
                    BORDER_WIDTH,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));

            BorderSourceRectangles.Add(
                (int)BorderCorners.NW,
                new Rectangle(
                    0,
                    0,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));

            BorderSourceRectangles.Add(
                (int)BorderCorners.NE,
                new Rectangle(
                    BORDER_WIDTH * 3,
                    0,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));

            BorderSourceRectangles.Add(
                (int)BorderCorners.SW,
                new Rectangle(
                    0,
                    BORDER_WIDTH * 3,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));

            BorderSourceRectangles.Add(
                (int)BorderCorners.SE,
                new Rectangle(
                    BORDER_WIDTH * 3,
                    BORDER_WIDTH * 3,
                    BORDER_WIDTH,
                    BORDER_WIDTH
                    ));
        }

        //this class depends on the menus being a multiple of 
        //the BORDER_WIDTH value, so snap to that value
        private void SnapMenuSize()
        {
            MenuRectangle = new Rectangle(
                MenuRectangle.X,
                MenuRectangle.Y,
                MenuRectangle.Width - (MenuRectangle.Width % BORDER_WIDTH),
                MenuRectangle.Height - (MenuRectangle.Height % BORDER_WIDTH));
        }

        #endregion

        #region XNA Methods

        //Sets position relative to screen
        //world placement (respecting the camera) is done in Draw
        public void SetPosition(MenuPlacementX x, MenuPlacementY y)
        {
            float xpos = 0;
            float ypos = 0;

            switch (x)
            {
                case MenuPlacementX.Left:
                    xpos = BORDER_WIDTH / 2;
                    break;

                case MenuPlacementX.Center:
                    xpos = (screenRect.Width / 2) - (this.MenuRectangle.Width / 2);
                    break;

                case MenuPlacementX.Right:
                    xpos = screenRect.Width - this.MenuRectangle.Width - BORDER_WIDTH / 2;
                    break;
            }

            switch (y)
            {
                case MenuPlacementY.Top:
                    ypos = BORDER_WIDTH / 2;
                    break;

                case MenuPlacementY.Center:
                    ypos = (screenRect.Height / 2) - (this.MenuRectangle.Height / 2);
                    break;

                case MenuPlacementY.Bottom:
                    ypos = screenRect.Height - this.MenuRectangle.Height - BORDER_WIDTH / 2;
                    break;
            }

            SetPosition((int)xpos, (int)ypos);
        }

        public void SetPosition(int x, int y)
        {
            MenuRectangle = new Rectangle(x, y, MenuRectangle.Width, MenuRectangle.Height);
        }

        public void SetSize(int w, int h)
        {
            MenuRectangle = new Rectangle(MenuRectangle.X, MenuRectangle.Y, w, h);
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        public void Kill()
        {
            Enabled = false;
            Visible = false;
            IsAlive = false;
            if (IsModal)
                OnPauseStateChanged(false);
        }

        public override void Draw(GameTime gameTime)
        {
            Draw(gameTime, null);
        }

        public virtual void Draw(GameTime gameTime, Camera camera)
        {
            if (Visible && Enabled)
            {
                spriteBatch.Draw(
                    BackgroundTexture,
                    new Rectangle(
                        MenuRectangle.X + BORDER_WIDTH / 2,
                        MenuRectangle.Y + BORDER_WIDTH / 2,
                        BackgroundTexture.Width,
                        BackgroundTexture.Height),
                    new Color(255,255,255,200));

                if (DisplayBorders)
                    spriteBatch.Draw(
                        BorderTexture,
                        MenuRectangle,
                        Color.White);
            }

            base.Draw(gameTime);
        }

        private Texture2D BuildBorderTexture()
        {
            SpriteBatch borderSpriteBatch = new SpriteBatch(Game.GraphicsDevice);
            RenderTarget2D rt = new RenderTarget2D(
                Game.GraphicsDevice,
                MenuRectangle.Width + BORDER_WIDTH,
                MenuRectangle.Height + BORDER_WIDTH);

            Game.GraphicsDevice.SetRenderTarget(rt);
            Game.GraphicsDevice.Clear(Color.Transparent);
            
            borderSpriteBatch.Begin();

            Rectangle borderCellDestRect = new Rectangle(
                0, 0, BORDER_WIDTH, BORDER_WIDTH);

            SBorderYCoord = BorderRectangle.Height - BORDER_WIDTH;
            EBorderXCoord = BorderRectangle.Width - BORDER_WIDTH;

            //North & South borders
            for (int x = 0, cnt = BorderRectangle.Width / BORDER_WIDTH - 2; x < cnt; x++)
            {
                borderCellDestRect.X += BORDER_WIDTH;

                borderSpriteBatch.Draw(
                    MenuBorderSourceTexture,
                    borderCellDestRect,
                    BorderSourceRectangles[(int)BorderSides.Top],
                    Color.White);

                borderCellDestRect.Y = SBorderYCoord;

                borderSpriteBatch.Draw(
                    MenuBorderSourceTexture,
                    borderCellDestRect,
                    BorderSourceRectangles[(int)BorderSides.Bottom],
                    Color.White);

                borderCellDestRect.Y = 0;
            }

            //East & West Borders
            borderCellDestRect.X = 0;
            borderCellDestRect.Y = 0;

            for (int y = 0, cnt = BorderRectangle.Height / BORDER_WIDTH - 2; y < cnt; y++)
            {
                borderCellDestRect.Y += BORDER_WIDTH;

                borderSpriteBatch.Draw(
                    MenuBorderSourceTexture,
                    borderCellDestRect,
                    BorderSourceRectangles[(int)BorderSides.Left],
                    Color.White);

                borderCellDestRect.X = EBorderXCoord;

                borderSpriteBatch.Draw(
                    MenuBorderSourceTexture,
                    borderCellDestRect,
                    BorderSourceRectangles[(int)BorderSides.Right],
                    Color.White);

                borderCellDestRect.X = 0;
            }

            //NW corner
            borderCellDestRect.X = 0;
            borderCellDestRect.Y = 0;

            borderSpriteBatch.Draw(
                MenuBorderSourceTexture,
                borderCellDestRect,
                BorderSourceRectangles[(int)BorderCorners.NW],
                Color.White);

            //NE corner
            borderCellDestRect.X = BorderRectangle.Width - BORDER_WIDTH;
            borderCellDestRect.Y = 0;

            borderSpriteBatch.Draw(
                MenuBorderSourceTexture,
                borderCellDestRect,
                BorderSourceRectangles[(int)BorderCorners.NE],
                Color.White);

            //SW corner
            borderCellDestRect.X = 0;
            borderCellDestRect.Y = BorderRectangle.Height - BORDER_WIDTH;

            borderSpriteBatch.Draw(
                MenuBorderSourceTexture,
                borderCellDestRect,
                BorderSourceRectangles[(int)BorderCorners.SW],
                Color.White);

            //SE corner
            borderCellDestRect.X = BorderRectangle.Width - BORDER_WIDTH;
            borderCellDestRect.Y = BorderRectangle.Height - BORDER_WIDTH;

            borderSpriteBatch.Draw(
                MenuBorderSourceTexture,
                borderCellDestRect,
                BorderSourceRectangles[(int)BorderCorners.SE],
                Color.White);

            borderSpriteBatch.End();

            Game.GraphicsDevice.SetRenderTarget(null);

            return rt;
        }

        #endregion

        #region Event Methods

        public virtual void OnPauseStateChanged(bool isPaused)
        {
            if (PauseStateChanged != null)
                PauseStateChanged(this, isPaused);
        }

        #endregion
    }
}